package com.pms.api;

import com.alipay.api.AlipayApiException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.pms.config.AliPayConfig;
import com.pms.entity.PayConfigWx;
import com.pms.exception.RRException;
import com.pms.service.IPayConfigAliService;
import com.pms.service.IPayConfigWxService;
import com.pms.service.IThirdPayService;
import com.pms.util.MD5Utils;
import com.pms.util.TenpayUtil;
import com.pms.util.WXUtil;
import com.pms.util.XMLUtil;
import com.pms.util.weixin.WxApi;
import com.pms.util.weixin.WxApiClient;
import net.sf.json.JSONArray;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * 聚合二维码扫码支付
 *
 * @author zyl
 * @create 2017-11-07 11:25
 **/
@Controller
@RequestMapping("/api/qrpay")
public class QrCodeController {
    Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    IThirdPayService thirdPayService;
    @Autowired
    IPayConfigAliService payConfigAliService;
    @Autowired
    IPayConfigWxService payConfigWxService;

//    @Autowired
//    IProShopService shopService;

    @Value("${qrcode.writePath}")
    private String qImgPath;

    static final String AppID = "wx874efc3b70b1baed";
    static final String AppSecret = "638d2c00870e42e5e49df2bed70cda23";
    private final static String GetOpenIdURL = "http://www.gxtzfpt.com/api/qrpay/getOpenId2";

   //#统一下单接口，
    String UNI_URL="https://api.mch.weixin.qq.com/pay/unifiedorder";

    /** 测试微信号的openId，这里固定写成我的微信openid，你们到时候自己编码获取 */
    private static final String openId = "ou4QqwKcPefSCbNK4T3wT4ZoyXII";

    /**
     * 生成二维码方法
     * @param model
     * @param response
     * @param shopId            暂时用shopId 根据业务处理
     * @param width
     * @param height
     * @throws Exception
     */
    @RequestMapping("/toQrPay")
    public void openQrPay(ModelMap model, HttpServletResponse response, String shopId, @RequestParam(value="width", defaultValue="80")
            int width , @RequestParam(value="height", defaultValue="80")int height) throws Exception {
        String content="http://www.gxtzfpt.com/api/qrpay/getPrice?shopId="+shopId;
        if(StringUtils.isBlank(shopId)){
            throw new RRException("店铺ID不能为空!");
        }
//        ProShop  shop = shopService.selectById(shopId);
//        if(shop==null){
//            throw new RRException("店铺信息不存在!");
//        }
        String format = "png";// 图像类型
        ServletOutputStream out = response.getOutputStream();
        Map<EncodeHintType,Object> config = new HashMap();
        config.put(EncodeHintType.CHARACTER_SET,"UTF-8");
        config.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        config.put(EncodeHintType.MARGIN, 0);
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE,width,height,config);
        MatrixToImageWriter.writeToStream(bitMatrix,format,out);
        System.out.println("二维码生成完毕，已经输出到页面中。");
    }

    /**
     * 跳转h5页面，输入金额页面
     * @param model
     * @param response
     * @param request
     * @param shopId
     * @return
     * @throws IOException
     * @throws AlipayApiException
     */
    @RequestMapping("/getPrice")
    public String getPrice(ModelMap model, HttpServletResponse response, HttpServletRequest request, String shopId)throws IOException, AlipayApiException {
        String view = "pmspay";
        String logPrefix = "【聚合扫码支付】";
        String qr_url="http://www.gxtzfpt.com/api/qrpay/getPrice?shopId="+shopId;
        String payUrl="http://www.gxtzfpt.com/api/qrpay/";
        String agent = request.getHeader("User-Agent").toLowerCase();
        int payType=0;
        if(StringUtils.isBlank(shopId)){
            throw new RRException("店铺ID不能为空!");
        }
//        ProShop  shop = shopService.selectById(shopId);
//
//        model.put("shop",shop);
        if (agent.indexOf("micromessenger") > 0) {
            payType=2;//支付方式是微信
            // 判断是否拿到openid，如果没有则去获取
            String openId = request.getParameter("openId");
            if (StringUtils.isNotBlank(openId)) {
                logger.info("{}openId：{}", logPrefix, openId);
                model.put("openId",openId);
                model.put("payUrl",payUrl+"aliPay");
            }else {
                String redirectUrl = qr_url;
                String url = GetOpenIdURL + "?redirectUrl=" + redirectUrl;
                logger.info("跳转URL={}", url);
                return "redirect:" + url;
            }

        } else if (agent.indexOf("alipayclient") > 0) {
            payType=1;//支付方式是支付宝
            model.put("payUrl",payUrl+"wxpay");
        }
        else {
           // throw new RRException("请使用支付宝或微信扫码!");
            payType=1;//支付方式是支付宝
            model.put("payUrl",payUrl+"wxpay");
        }
        model.put("payType",payType);//支付类型
        request.setAttribute("payType", payType);
//        request.setAttribute("shop", shop);
        System.out.println("-----------------");
//        logger.info(shop.toString() +":-----");
        return view;
    }

//@RequestMapping("/aliPay")
//public String aliPay(HttpServletRequest request, HttpServletResponse response, String totalPrice, String shopName)throws IOException, AlipayApiException {
//    String view = "tzfpay";
//    logger.info("aliPay---------");
//    // 支付对象封装
//    Product product = new Product();
//    product.setAttach("测试");
//    product.setBody("两个苹果八毛钱");
//    product.setFrontUrl("https");
//    product.setOutTradeNo(NumberUtil.getNu(NuType.newOrder));
//    product.setProductId("111111");
//    product.setSubject(shopName);
//    product.setTotalFee(totalPrice+"");
//    product.setPayWay("QUICK_WAP_PAY");
//    PayConfigAli payConfigAli = payConfigAliService.selectById(2);
//
//    String form = thirdPayService.aliPayMobile(product,payConfigAli);
//    response.setContentType("text/html;charset=UTF-8");
//    response.getWriter().write(form);//直接将完整的表单html输出到页面
//    response.getWriter().flush();
//    response.getWriter().close();
//     return view;
//
//}
    /**
     * 获取code
     * @return
     */
    @RequestMapping("/getOpenId2")
    public void getOpenId2(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.info("进入获取用户openID页面");
        String redirectUrl = request.getParameter("redirectUrl");
        String code = request.getParameter("code");
        logger.info("授权code={}",code);
        String openId = "";
        if(!StringUtils.isBlank(code)){//如果request中包括code，则是微信回调
            try {
                openId = WxApiClient.getOAuthOpenId(AppID, AppSecret, code);
                logger.info("调用微信返回openId={}", openId);
            } catch (Exception e) {
                logger.error(e+"调用微信查询openId异常");
            }
            if(redirectUrl.indexOf("?") > 0) {
                redirectUrl += "&openId=" + openId;
            }else {
                redirectUrl += "?openId=" + openId;
            }
            response.sendRedirect(redirectUrl);
        }else{//oauth获取code

            String redirectUrl4Vx = GetOpenIdURL + "?redirectUrl=" + redirectUrl;
            String url = WxApi.getOAuthCodeUrl(AppID,redirectUrl4Vx,"snsapi_base","FCP");
            logger.info("跳转URL={}", url);
            response.sendRedirect(url);
        }
    }


    /***
     * 调用统一下单接口获取预支付单号prepay_id，生成订单数据 以及 微信支付需要的签名等信息 传输到前端，进行调用JSAPI支付接口
     *
     *
     * @param shopName
     *            商品名称
     * @param totalPrice
     *            总金额(元)
     * @param request
     * @return
     */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/wxPay")
    public @ResponseBody
    String String(HttpServletRequest request, String shopName, String totalPrice, String openId) {
        String view = "tzfpay";
        String path = request.getContextPath();
        String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
        logger.info("aliPay---------");
        logger.info("basePath=" + basePath);
        PayConfigWx payConfigWx = payConfigWxService.selectById(1);
        if(StringUtils.isBlank(totalPrice)){
            throw new RRException("请输入支付金额！");
        }
        /** 总金额(分为单位) */
        int total = (int) (Double.parseDouble(totalPrice) * 100);

        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        /** 公众号APPID */
        parameters.put("appid", payConfigWx.getAppId());
        /** 商户号 */
        parameters.put("mch_id", payConfigWx.getMchId());
        /** 随机字符串 */
        parameters.put("nonce_str", WXUtil.getNonceStr());
        /** 商品名称 */
        parameters.put("body", shopName);

        /** 当前时间 yyyyMMddHHmmss */
        String currTime = TenpayUtil.getCurrTime();
        /** 8位日期 */
        String strTime = currTime.substring(8, currTime.length());
        /** 四位随机数 */
        String strRandom = TenpayUtil.buildRandom(4) + "";
        /** 订单号 */
        parameters.put("out_trade_no", strTime + strRandom);

        /** 订单金额以分为单位，只能为整数 */
        parameters.put("total_fee", total);
        /** 客户端本地ip */
        parameters.put("spbill_create_ip", request.getRemoteAddr());
        /** 支付回调地址 */
        parameters.put("notify_url",payConfigWx.getNotifyUrl() );
        /** 支付方式为JSAPI支付 */
        parameters.put("trade_type", "JSAPI");
        /** 用户微信的openid，当trade_type为JSAPI的时候，该属性字段必须设置 */
        parameters.put("openid", openId);

        /** MD5进行签名，必须为UTF-8编码，注意上面几个参数名称的大小写 */
        String sign = createSign("UTF-8", parameters);
        parameters.put("sign", sign);

        /** 生成xml结构的数据，用于统一下单接口的请求 */
        String requestXML = getRequestXml(parameters);
        logger.info("requestXML：" + requestXML);
        /** 开始请求统一下单接口，获取预支付prepay_id */

        HttpClient client = new HttpClient();
        PostMethod myPost = new PostMethod(UNI_URL);
        client.getParams().setSoTimeout(300 * 1000);
        String result = null;
        try {
            myPost.setRequestEntity(new StringRequestEntity(requestXML, "text/xml", "utf-8"));
            int statusCode = client.executeMethod(myPost);
            if (statusCode == HttpStatus.SC_OK) {
                BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
                byte[] bytes = new byte[1024];
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                int count = 0;
                while ((count = bis.read(bytes)) != -1) {
                    bos.write(bytes, 0, count);
                }
                byte[] strByte = bos.toByteArray();
                result = new String(strByte, 0, strByte.length, "utf-8");
                bos.close();
                bis.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        /** 需要释放掉、关闭连接 */
        myPost.releaseConnection();
        client.getHttpConnectionManager().closeIdleConnections(0);

        logger.info("请求统一支付接口的返回结果:");
        logger.info(result);
        try {
            /** 解析微信返回的信息，以Map形式存储便于取值 */
            Map<String, String> map = XMLUtil.doXMLParse(result);

            SortedMap<Object, Object> params = new TreeMap<Object, Object>();
            params.put("appId", payConfigWx.getAppId());
            params.put("timeStamp", "\"" + new Date().getTime() + "\"");
            params.put("nonceStr", WXUtil.getNonceStr());
            /**
             * 获取预支付单号prepay_id后，需要将它参与签名。
             * 微信支付最新接口中，要求package的值的固定格式为prepay_id=...
             */
            params.put("package", "prepay_id=" + map.get("prepay_id"));

            /** 微信支付新版本签名算法使用MD5，不是SHA1 */
            params.put("signType", "MD5");
            /**
             * 获取预支付prepay_id之后，需要再次进行签名，参与签名的参数有：appId、timeStamp、nonceStr、package、signType.
             * 主意上面参数名称的大小写.
             * 该签名用于前端js中WeixinJSBridge.invoke中的paySign的参数值
             */
            String paySign = createSign("UTF-8", params);
            params.put("paySign", paySign);

            /** 预支付单号，前端ajax回调获取。由于js中package为关键字，所以，这里使用packageValue作为key。 */
            params.put("packageValue", "prepay_id=" + map.get("prepay_id"));

            /** 付款成功后，微信会同步请求我们自定义的成功通知页面，通知用户支付成功 */
            params.put("sendUrl", basePath + "pay/paysuccess?totalPrice=" + totalPrice);
            /** 获取用户的微信客户端版本号，用于前端支付之前进行版本判断，微信版本低于5.0无法使用微信支付 */
            String userAgent = request.getHeader("user-agent");
            char agent = userAgent.charAt(userAgent.indexOf("MicroMessenger") + 15);
            params.put("agent", new String(new char[] { agent }));

            String json = JSONArray.fromObject(params).toString();
            return json;

        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return view;
    }

    /**
     * sign签名
     *
     * 作者: zhoubang 日期：2015年6月10日 上午9:31:24
     *
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();

        Set<Map.Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Map.Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            /** 如果参数为key或者sign，则不参与加密签名 */
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        /** 支付密钥必须参与加密，放在字符串最后面 */
        sb.append("key=" + "fc06fff13e3801e6d65f193e38b11fec");
        /** 记得最后一定要转换为大写 */
        String sign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

    /**
     * 将请求参数转换为xml格式的string
     *
     * 作者: zhoubang 日期：2015年6月10日 上午9:25:51
     *
     * @param parameters
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set<Map.Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Map.Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next();
            String k = (String) entry.getKey();
            String v = entry.getValue() + "";
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /***
     * 付款成功回调处理
     *
     * 作者: zhoubang 日期：2015年6月10日 上午9:25:29
     *
     * @param request
     * @param response
     * @throws IOException
     * @throws JDOMException
     */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "pay")
    public @ResponseBody
    void notify_success(HttpServletRequest request,
                        HttpServletResponse response) throws IOException, JDOMException {
        logger.info("微信支付成功调用回调URL");
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        logger.info("~~~~~~~~~~~~~~~~付款成功~~~~~~~~~");
        outSteam.close();
        inStream.close();

        /** 支付成功后，微信回调返回的信息 */
        String result = new String(outSteam.toByteArray(), "utf-8");
        logger.info("微信返回的订单支付信息:" + result);
        Map<Object, Object> map = XMLUtil.doXMLParse(result);

        // 用于验签
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        for (Object keyValue : map.keySet()) {
            /** 输出返回的订单支付信息 */
            logger.info(keyValue + "=" + map.get(keyValue));
            if (!"sign".equals(keyValue)) {
                parameters.put(keyValue, map.get(keyValue));
            }
        }
        if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
            // 先进行校验，是否是微信服务器返回的信息
            String checkSign = createSign("UTF-8", parameters);
            logger.info("对服务器返回的结果进行签名：" + checkSign);
            logger.info("服务器返回的结果签名：" + map.get("sign"));
            if (checkSign.equals(map.get("sign"))) {// 如果签名和服务器返回的签名一致，说明数据没有被篡改过
                logger.info("签名校验成功，信息合法，未被篡改过");


                //告诉微信服务器，我收到信息了，不要再调用回调方法了

                /**如果不返回SUCCESS的信息给微信服务器，则微信服务器会在一定时间内，多次调用该回调方法，如果最终还未收到回馈，微信默认该订单支付失败*/
                /** 微信默认会调用8次该回调地址 */
                /*
                 * 【需要注意】：
                 *      后期很多朋友都反应说，微信会一直请求这个回调地址，也都是使用的是 response.getWriter().write(setXML("SUCCESS", ""));
                 *      百思不得其解，最终发现处理办法。其实不能直接使用response.getWriter()返回结果，这样微信是接收不到的。
                 *      只能使用OutputStream流的方式返回结果给微信。
                 *      切记！！！！
                 * */
                OutputStream outputStream = null;
                try {
                    outputStream = response.getOutputStream();
                    outputStream.flush();
                    outputStream.write(setXML("SUCCESS", "").getBytes());
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    try {
                        outputStream.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                //response.getWriter().write(setXML("SUCCESS", ""));

                logger.info("-------------" + setXML("SUCCESS", ""));
            }
        }
    }

    /**
     * 发送xml格式数据到微信服务器 告知微信服务器回调信息已经收到。
     *
     * 作者: zhoubang 日期：2015年6月10日 上午9:27:33
     *
     * @param return_code
     * @param return_msg
     * @return
     */
    public static String setXML(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code
                + "]]></return_code><return_msg><![CDATA[" + return_msg
                + "]]></return_msg></xml>";
    }

    /**
     * 支付成功请求的地址URL，告知用户已经支付成功
     *
     * 作者: zhoubang 日期：2015年6月10日 上午10:37:35
     *
     * @param request
     * @param response
     * @param totalPrice
     *            金额单位为元
     * @return
     */
    @RequestMapping("paysuccess")
    public ModelAndView paysuccess(HttpServletRequest request,
                                   HttpServletResponse response, double totalPrice) {
        ModelAndView mav = new ModelAndView("forward:" + "/paysuccess.jsp");
        mav.addObject("money", totalPrice);
        return mav;
    }


    /**
     * 聚合支付跳转/支付宝/微信支付
     * @param request
     * @param response
     * @throws IOException
     * @throws AlipayApiException
     */
    @RequestMapping("/qrcallback")
    public String qrcallback(ModelMap model, HttpServletRequest request, HttpServletResponse response) throws IOException, AlipayApiException {

        String view = "paysuccess";

        return view;
    }
}
